package org.carpediem.conman.rest;

import com.sun.jersey.multipart.MultiPart;
import org.carpediem.conman.Util;
import org.carpediem.conman.dao.ContactDao;
import org.carpediem.conman.dao.DocumentDao;
import org.carpediem.conman.dao.FirmDao;
import org.carpediem.conman.dao.NoteDao;
import org.carpediem.conman.entity.*;
import org.carpediem.schema.conman.FirmInfo;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
import java.net.URI;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Mark Petrovic
 */
public class AbstractResource extends RestBase {
    private final static Logger logger = Logger.getLogger(AbstractResource.class.getName());

    protected final ContactDao contactDao;
    protected final DocumentDao documentDao;
    protected final NoteDao noteDao;
    protected final FirmDao firmDao;
    protected final ServletContext servletContext;
    protected final ClassPathXmlApplicationContext applicationContext;

    protected AbstractResource(@Context UriInfo uriInfo, @Context ServletContext servletContext) {
        super(uriInfo);
        this.servletContext = servletContext;
        this.applicationContext = (ClassPathXmlApplicationContext) this.servletContext.getAttribute("springContext");
        this.contactDao = (ContactDao) applicationContext.getBean("contactDao");
        this.documentDao = (DocumentDao) applicationContext.getBean("documentDao");
        this.noteDao = (NoteDao) applicationContext.getBean("noteDao");
        this.firmDao = (FirmDao) applicationContext.getBean("firmDao");
    }

    protected void requireNewContact(String emailAddress) {
        Contact contact = contactDao.getByEmail(emailAddress);
        if (contact != null) {
            throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE).entity("User already exists: " + emailAddress).build());
        }
    }

    protected Contact requireContactExists(long contactId) {
        Contact contact = contactDao.get(contactId);
        if (contactDao.get(contactId) == null) {
            throwNotFound(contactId);
        }
        return contact;
    }

    protected void requireDocumentNotBound(Contact contact, byte[] buffer) {
        List<Document> docList = documentDao.getByOwner(contact);
        for (Document d : docList) {
            if (d.getHexMD5().equals(Util.hexDigest(buffer))) {
                logger.info("This document is already attached to this contact: " + contact.getEmailAddress() + ", doc digest: " + d.getHexMD5());
                throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE)
                        .entity("Document is already bound to this contact: " + contact.getEmailAddress() + ", documentId: " + d.getId()).build());
            }
        }
    }

    protected Document requireDocumentExists(long documentId) {
        Document document = documentDao.get(documentId);
        if (document == null) {
            throwNotFound(documentId);
        }
        return document;
    }

    protected Note requireNoteExists(long noteId) {
        Note note = noteDao.get(noteId);
        if (note == null) {
            throwNotFound(noteId);
        }
        return note;
    }

    protected void requireContactOwnsDocument(Contact contact, Document document) {
        boolean owns = false;
        for (Document d : contact.getDocument()) {
            if (d.getId() == document.getId()) {
                owns = true;
                break;
            }
        }
        if (!owns) {
            throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE)
                    .entity("Contact does not own this document " + contact.getId() + ", " + document.getId())
                    .build());
        }
    }

    protected void requireContactOwnsNote(Contact contact, Note note) {
        boolean owns = false;
        for (Note d : contact.getNote()) {
            if (d.getId() == note.getId()) {
                owns = true;
                break;
            }
        }
        if (!owns) {
            throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE)
                    .entity("Contact does not own this note " + contact.getId() + ", " + note.getId())
                    .build());
        }
    }

    protected void requireMultiParts(MultiPart mp) {
        if (mp.getBodyParts().size() < 1) {
            throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE).entity("Multipart content must have > 0 parts").build());
        }
    }

    protected void requireNewFirm(FirmInfo fInfo) {
        String url = fInfo.getFirm().getUrl();
        Firm firm = firmDao.getByUrl(url);
        if (firm != null) {
            throw new WebApplicationException(Response.status(Response.Status.NOT_ACCEPTABLE).entity("Firm already exists: " + url).build());
        }
    }

    protected void throwNotFound(long id) {
        throw new WebApplicationException(Response
                .status(Response.Status.NOT_ACCEPTABLE)
                .entity("Entity not found: " + id)
                .build());
    }

    protected URI uri(Contact e) {
        return uriInfo.getBaseUriBuilder().path("contacts/{contactId}").build(e.getId());
    }

    protected <T> String toXml(T instance) {
        StringWriter sWriter = new StringWriter();
        try {
            JAXBContext jc = JAXBContext.newInstance(instance.getClass());
            Marshaller m = jc.createMarshaller();
            m.marshal(instance, sWriter);
        } catch (JAXBException e) {
            e.printStackTrace();
        }

        return sWriter.toString();
    }

    protected <U extends DomainObjectBag, T> U entityToBag(List<T> theList, Class<U> bagClass) {
        U bag = null;
        try {
            bag = bagClass.newInstance();
            for (T t : theList) {
                bag.add(t);
            }
        } catch (InstantiationException e) {
            logger.log(Level.WARNING, "error instantiating bag class", e);
        } catch (IllegalAccessException e) {
            logger.log(Level.WARNING, "error instantiating bag class", e);
        }

        return bag;
    }
}
